use futures::future::BoxFuture;
use std::{
    collections::HashMap,
    sync::{Arc, RwLock},
};

#[derive(Debug, Clone)]
pub struct Context {
    pub id: u32,
    pub name: String,
    pub url: String,
    pub payload: Vec<u8>,
}

pub trait FromContext: Send + Sync {
    fn from_context(context: &Context) -> Self;
}

pub trait Handler<T>: Send + Sync {
    fn call(&self, context: Context) -> BoxFuture<'static, ()>;
}

type HandlerBox = dyn Fn(Context) -> BoxFuture<'static, ()> + Send + Sync;

pub struct HandlerManager {
    handlers: Arc<RwLock<HashMap<&'static str, Arc<HandlerBox>>>>,
}

impl HandlerManager {
    pub fn new() -> Self {
        Self {
            handlers: Arc::new(RwLock::new(HashMap::new())),
        }
    }

    pub fn register<T, H>(&self, name: &'static str, handler: H)
    where
        H: Handler<T> + 'static,
        T: Send,
    {
        let mut handlers = self.handlers.write().unwrap();

        let wrapper = move |ctx: Context| handler.call(ctx);

        handlers.insert(name, Arc::new(wrapper));
    }

    pub async fn execute(&self, name: &'static str, ctx: Context) {
        if let Some(handler) = self.get_handler(name) {
            handler(ctx).await;
        }
    }

    fn get_handler(&self, name: &'static str) -> Option<Arc<HandlerBox>> {
        let handlers = self.handlers.read().unwrap();
        handlers.get(name).cloned()
    }
}

macro_rules! all_the_tuples {
    ($name:ident) => {
        $name!([]);
        $name!([T1]);
        $name!([T1, T2]);
        $name!([T1, T2, T3]);
        $name!([T1, T2, T3, T4]);
        $name!([T1, T2, T3, T4, T5]);
        $name!([T1, T2, T3, T4, T5, T6]);
        $name!([T1, T2, T3, T4, T5, T6, T7]);
        $name!([T1, T2, T3, T4, T5, T6, T7, T8]);
        $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9]);
        $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]);
        $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]);
        $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]);
    };
}

macro_rules! impl_handler {
    ([$($ty:ident),*]) => {
        impl<F, Fut, $($ty,)*> Handler<($($ty,)*)> for F
        where
            F: Fn($($ty,)*) -> Fut + Send + Sync+Copy+ 'static,
            Fut: Future<Output=()> + Send + 'static,
            $( $ty: FromContext + Send,)*
        {
            fn call(&self, context: Context) -> BoxFuture<'static, ()> {
                let fun = *self;
                Box::pin(async move {
                    fun($( $ty::from_context(&context), )*).await;
                })
            }
        }
    };
}

all_the_tuples!(impl_handler);

pub struct Params(String);

impl FromContext for Params {
    fn from_context(context: &Context) -> Self {
        Params(context.name.clone())
    }
}

pub struct Id(u32);

impl FromContext for Id {
    fn from_context(context: &Context) -> Self {
        Id(context.id)
    }
}

async fn hello() {
    println!("hello start");
}

async fn hello2(Params(name): Params, Id(id): Id) {
    println!("hello {}, your id:{}", name, id);
}

#[cfg(test)]
mod test {
    use crate::extractor_demo::async_demo::HandlerManager;

    use super::{Context, hello, hello2};

    #[tokio::test]
    async fn trigger_async_test() {
        let context = Context {
            id: 1,
            name: "Test".to_string(),
            url: "test.com".to_string(),
            payload: Vec::new(),
        };

        let ctx = Context {
            id: 2,
            name: "LoongWang".to_string(),
            url: "loongWang.com".to_string(),
            payload: Vec::new(),
        };

        let manager = HandlerManager::new();
        manager.register("hello", hello);
        manager.register("hello2", hello2);

        manager.execute("hello", context.clone()).await;
        manager.execute("hello2", context.clone()).await;

        manager.execute("hello", ctx.clone()).await;
        manager.execute("hello2", ctx.clone()).await;
    }
}
